﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace huqiang.Communication
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
    public struct UserMsg
    {
        /// <summary>
        /// 数据类型
        /// </summary>
        public byte DataType;
        /// <summary>
        /// 指令类型
        /// </summary>
        public byte CmdType;
        /// <summary>
        /// 指令
        /// </summary>
        public UInt32 Cmd;
        /// <summary>
        /// 错误码
        /// </summary>
        public UInt16 Error;
        /// <summary>
        /// 当前分卷
        /// </summary>
        public byte CurPart;
        /// <summary>
        /// 总计分卷
        /// </summary>
        public byte AllPart;
        /// <summary>
        /// 消息id
        /// </summary>
        public int MsgID;
        public unsafe static int Size = sizeof(UserMsg);
    }
    public class KcpUser : KcpLink
    {
        class BigMsg
        {
            public UserMsg Msg;
            public int rcvParts;
            public byte[][] Parts;
            public long lastTime;
        }
        List<BigMsg> msgs = new List<BigMsg>();
        public KcpFileTransfer FileTransfer { get; private set; }
        /// <summary>
        /// 打开文件传输功能
        /// </summary>
        public void OpenFileTransfer()
        {
            if (FileTransfer == null)
                FileTransfer = new KcpFileTransfer(this);
        }
        public override void Awake()
        {
            Send(EnvelopeType.Mate, 0xffff, null, 255);
        }
        public sealed override void Dispatch(byte[] dat, int startIndex, int len)
        {
            if (dat == null)
                return;
            if (dat[startIndex] == EnvelopeType.File)
            {
                if (FileTransfer != null)
                {
                    FileTransfer.Dispatch(dat, startIndex, len);
                }
                else if (AllowFileTransfer)
                {
                    FileTransfer = new KcpFileTransfer(this);
                    FileTransfer.Dispatch(dat, startIndex, len);
                }
            }
            else
            {
                UserMsg msg;
                string str = null;
                byte[] buf = null;
                unsafe
                {
                    fixed (byte* bp = &dat[startIndex])
                    {
                        msg = *(UserMsg*)bp;
                        len = len - UserMsg.Size;
                        if (msg.AllPart > 128)
                            return;
                        if (msg.AllPart == 1)
                        {
                            if (msg.DataType == EnvelopeType.String | msg.DataType == EnvelopeType.Json)
                            {
                                if (len > 0)
                                {
                                    str = Encoding.UTF8.GetString(bp + UserMsg.Size, len);
                                }
                            }
                            else
                            {
                                if (len > 0)
                                {
                                    buf = new byte[len];
                                    Marshal.Copy((IntPtr)(bp + UserMsg.Size), buf, 0, len);
                                }
                            }
                        }
                        else if (msg.AllPart > 1 & msg.AllPart <= 32)
                        {
                            if (len > 0)
                            {
                                buf = new byte[len];
                                Marshal.Copy((IntPtr)(bp + UserMsg.Size), buf, 0, len);
                            }
                        }
                    }
                }
                if (msg.AllPart == 1)
                {
                    if (msg.DataType == EnvelopeType.String | msg.DataType == EnvelopeType.Json)
                    {
                        HandleMsg(ref msg, str, null);
                        //Console.WriteLine(str);
                    }
                    else
                    {
                        HandleMsg(ref msg, null, buf);
                    }
                }
                else if (msg.AllPart > 1 & msg.AllPart <= 32)
                {
                    ProBigMsg(ref msg, buf);
                }
                ClearTimeOut();
            }
        }
        public void ClearTimeOut()
        {
            long now = System.Diagnostics.Stopwatch.GetTimestamp();//DateTime.Now.Ticks;
            for (int i = msgs.Count - 1; i >= 0; i--)
            {
                if (now - msgs[i].lastTime > 10 * 1000 * 10000)//超过10秒没有接受到消息了
                {
                    msgs.RemoveAt(i);
                }
            }
        }
        void ProBigMsg(ref UserMsg msg, byte[] dat)
        {
            for (int i = 0; i < msgs.Count; i++)
            {
                var item = msgs[i];
                if (item.Msg.MsgID == msg.MsgID)
                {
                    item.Parts[msg.CurPart] = dat;
                    item.rcvParts++;
                    item.lastTime = System.Diagnostics.Stopwatch.GetTimestamp();//DateTime.Now.Ticks;

                    if (item.rcvParts >= msg.AllPart)
                    {
                        msgs.RemoveAt(i);
                        MemoryStream ms = new MemoryStream();
                        for (int j = 0; j < item.Parts.Length; j++)
                        {
                            ms.Write(item.Parts[j], 0, item.Parts[j].Length);
                        }
                        byte[] buf = ms.ToArray();
                        ms.Dispose();
                        if (msg.DataType == EnvelopeType.String | msg.DataType == EnvelopeType.Json)
                        {
                            string str = Encoding.UTF8.GetString(buf);
                            HandleMsg(ref msg, str, null);
                        }
                        else
                        {
                            HandleMsg(ref msg, null, buf);
                        }
                    }
                    return;
                }
            }
            BigMsg big = new BigMsg();
            big.Msg = msg;
            big.rcvParts = 1;
            big.Parts = new byte[msg.AllPart][];
            big.Parts[msg.CurPart] = dat;
            big.lastTime = System.Diagnostics.Stopwatch.GetTimestamp();//DateTime.Now.Ticks;
            msgs.Add(big);
        }
        public virtual void HandleMsg(ref UserMsg msg, string str, byte[] dat)
        {
            if (msg.DataType == EnvelopeType.Mate)
            {
                if (msg.Cmd == 0xffff & msg.CmdType == 255)
                {
                    Connect();
                }
            }
        }
        static int MsgID = 0;
        public static byte[][] EncodingData(byte dataType, UInt32 cmd, byte[] dat, byte cmdType = 0, UInt16 error = 0)
        {
            MsgID++;
            int mid = MsgID;
            int c = 0;
            if (dat != null)
                c = dat.Length;
            int ms = DataPacker.FragmentDataSize * 32;
            int max = ms - UserMsg.Size;
            int allpart = c / max;
            int ma = allpart;
            int r = c % max;
            if (r > 0)
                allpart++;
            byte[][] bufs = new byte[allpart][];
            for (int i = 0; i < ma; i++)
            {
                byte[] b = new byte[ms];
                unsafe
                {
                    fixed (byte* bp = &b[0])
                    {
                        UserMsg* ip = (UserMsg*)bp;
                        ip->DataType = dataType;
                        ip->CmdType = cmdType;
                        ip->Cmd = cmd;
                        ip->Error = error;
                        ip->CurPart = (byte)i;
                        ip->AllPart = (byte)allpart;
                        ip->MsgID = mid;
                    }
                    //Array.Copy(dat, max * i, b, UserMsg.Size, max);
                    int src = max * i;
                    int des = UserMsg.Size;
                    for (int j = 0; j < max; j++)
                    {
                        b[des] = dat[src];
                        des++;
                        src++;
                    }
                    bufs[i] = b;
                }
            }
            byte[] buf = new byte[r + UserMsg.Size];
            unsafe
            {
                fixed (byte* bp = &buf[0])
                {
                    UserMsg* ip = (UserMsg*)bp;
                    ip->DataType = dataType;
                    ip->CmdType = cmdType;
                    ip->Cmd = cmd;
                    ip->Error = error;
                    ip->CurPart = (byte)ma;
                    ip->AllPart = (byte)allpart;
                    ip->MsgID = mid;
                }
                //Array.Copy(dat, max * ma, buf, UserMsg.Size, r);
                int src = max * ma;
                int des = UserMsg.Size;
                for (int j = 0; j < r; j++)
                {
                    buf[des] = dat[src];
                    des++;
                    src++;
                }
            }
            bufs[ma] = buf;
            return bufs;
        }
        public static byte[] EncodingNull(byte dataType, UInt32 cmd, byte cmdType, UInt16 error)
        {
            byte[] buf = new byte[UserMsg.Size];
            unsafe
            {
                fixed (byte* bp = &buf[0])
                {
                    UserMsg* ip = (UserMsg*)bp;
                    ip->DataType = dataType;
                    ip->CmdType = cmdType;
                    ip->Cmd = cmd;
                    ip->Error = error;
                    ip->AllPart = 1;
                    ip->CurPart = 0;
                    ip->MsgID = 0;
                }
            }
            return buf;
        }
        public virtual void Send(byte dataType, UInt32 cmd, byte[] dat, byte cmdType = 0, UInt16 error = 0)
        {
            if (dat == null)
            {
                Post(EncodingNull(dataType, cmd, cmdType, error));
            }
            else
            {
                byte[][] bufs = EncodingData(dataType, cmd, dat, cmdType, error);
                for (int i = 0; i < bufs.Length; i++)
                    Post(bufs[i]);
            }
        }
        public virtual void SendString(UInt32 cmd, string str, byte cmdType = 0, UInt16 error = 0)
        {
            if (str == null || str == "")
            {
                Post(EncodingNull(EnvelopeType.String, cmd, cmdType, error));
            }
            else
            {
                byte[][] bufs = EncodingData(EnvelopeType.String, cmd, Encoding.UTF8.GetBytes(str), cmdType, error);
                for (int i = 0; i < bufs.Length; i++)
                    Post(bufs[i]);
            }
        }
        public virtual void SendJson(UInt32 cmd, string str, byte cmdType = 0, UInt16 error = 0)
        {
            if (str == null || str == "")
            {
                Post(EncodingNull(EnvelopeType.Json, cmd, cmdType, error));
            }
            else
            {
                byte[][] bufs = EncodingData(EnvelopeType.Json, cmd, Encoding.UTF8.GetBytes(str), cmdType, error);
                for (int i = 0; i < bufs.Length; i++)
                    Post(bufs[i]);
            }
        }
        public override void ContinuousSend()
        {
            if (FileTransfer != null)
                FileTransfer.ContinuousSend();
        }
    }
}
